#!/bin/bash

# Copyright (c) 2021  Red Hat, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

VERSION="1.0"

# Warning! Be sure to download the latest version of this script from its primary source:

ARTICLE="https://access.redhat.com/security/vulnerabilities/RHSB-2021-003"

# DO NOT blindly trust any internet sources and NEVER do `curl something | bash`!

# This script is meant for simple detection of the vulnerability. Feel free to modify it for your
# environment or needs. For more advanced detection, consider Red Hat Insights:
# https://access.redhat.com/products/red-hat-insights#getstarted

# Checking against the list of vulnerable packages is necessary because of the way how features
# are back-ported to older versions of packages in various channels.

VULNERABLE_VERSIONS=(
    'grub2-2.00-22.10.el7'
    'grub2-2.02-0.2.10.el7'
    'grub2-2.02-0.16.el7'
    'grub2-2.02-0.17.el7_1.4'
    'grub2-2.02-0.17.el7_1.6'
    'grub2-2.02-0.29.el7'
    'grub2-2.02-0.33.el7_2'
    'grub2-2.02-0.34.el7_2.2'
    'grub2-2.02-0.34.el7_2'
    'grub2-2.02-0.44.el7'
    'grub2-2.02-0.64.el7'
    'grub2-2.02-0.65.el7_4.2'
    'grub2-2.02-0.65.el7_4.3'
    'grub2-2.02-0.65.el7_5.4'
    'grub2-2.02-0.75.el7'
    'grub2-2.02-0.76.el7.1'
    'grub2-2.02-0.76.el7'
    'grub2-2.02-0.76.el7_6.2'
    'grub2-2.02-0.80.el7'
    'grub2-2.02-0.81.el7'
    'grub2-2.02-0.82.el7'
    'grub2-2.02-0.86.el7'
    'grub2-2.02-0.86.el7_2'
    'grub2-2.02-0.86.el7_4'
    'grub2-2.02-0.86.el7_6'
    'grub2-2.02-0.86.el7_7'
    'grub2-2.02-0.86.el7_8'
    'grub2-2.02-0.87.el7'
    'grub2-2.02-66.el8'
    'grub2-2.02-66.el8_0.1'
    'grub2-2.02-78.el8'
    'grub2-2.02-78.el8_1.1'
    'grub2-2.02-81.el8'
    'grub2-2.02-82.el8_2.1'
    'grub2-2.02-84.el8'
    'grub2-2.02-87.el8_0'
    'grub2-2.02-87.el8_1.1'
    'grub2-2.02-87.el8_1'
    'grub2-2.02-87.el8_2.2'
    'grub2-2.02-87.el8_2'
    'grub2-2.02-88.el8'
    'grub2-2.02-90.el8'
    'grub2-pc-2.02-0.64.el7'
    'grub2-pc-2.02-0.65.el7_4.2'
    'grub2-pc-2.02-0.65.el7_4.3'
    'grub2-pc-2.02-0.65.el7_5.4'
    'grub2-pc-2.02-0.76.el7.1'
    'grub2-pc-2.02-0.76.el7'
    'grub2-pc-2.02-0.76.el7_6.2'
    'grub2-pc-2.02-0.80.el7'
    'grub2-pc-2.02-0.81.el7'
    'grub2-pc-2.02-0.86.el7'
    'grub2-pc-2.02-0.86.el7_2'
    'grub2-pc-2.02-0.86.el7_4'
    'grub2-pc-2.02-0.86.el7_6'
    'grub2-pc-2.02-0.86.el7_7'
    'grub2-pc-2.02-0.86.el7_8'
    'grub2-pc-2.02-0.87.el7'
    'grub2-pc-2.02-66.el8'
    'grub2-pc-2.02-66.el8_0.1'
    'grub2-pc-2.02-78.el8'
    'grub2-pc-2.02-78.el8_1.1'
    'grub2-pc-2.02-81.el8'
    'grub2-pc-2.02-82.el8_2.1'
    'grub2-pc-2.02-84.el8'
    'grub2-pc-2.02-87.el8_0'
    'grub2-pc-2.02-87.el8_1.1'
    'grub2-pc-2.02-87.el8_1'
    'grub2-pc-2.02-87.el8_2.2'
    'grub2-pc-2.02-87.el8_2'
    'grub2-pc-2.02-88.el8'
    'grub2-pc-2.02-90.el8'
    'grub2-efi-2.02-0.2.10.el7'
    'grub2-efi-2.02-0.16.el7'
    'grub2-efi-2.02-0.17.el7_1.4'
    'grub2-efi-2.02-0.17.el7_1.6'
    'grub2-efi-2.02-0.29.el7'
    'grub2-efi-2.02-0.33.el7_2'
    'grub2-efi-2.02-0.34.el7_2.2'
    'grub2-efi-2.02-0.34.el7_2'
    'grub2-efi-2.02-0.44.el7'
    'grub2-efi-x64-2.02-0.64.el7'
    'grub2-efi-x64-2.02-0.65.el7_4.2'
    'grub2-efi-x64-2.02-0.65.el7_4.3'
    'grub2-efi-x64-2.02-0.65.el7_5.4'
    'grub2-efi-x64-2.02-0.76.el7.1'
    'grub2-efi-x64-2.02-0.76.el7'
    'grub2-efi-x64-2.02-0.76.el7_6.2'
    'grub2-efi-x64-2.02-0.80.el7'
    'grub2-efi-x64-2.02-0.81.el7'
    'grub2-efi-x64-2.02-0.86.el7'
    'grub2-efi-x64-2.02-0.86.el7_2'
    'grub2-efi-x64-2.02-0.86.el7_4'
    'grub2-efi-x64-2.02-0.86.el7_6'
    'grub2-efi-x64-2.02-0.86.el7_7'
    'grub2-efi-x64-2.02-0.86.el7_8'
    'grub2-efi-x64-2.02-0.87.el7'
    'grub2-efi-x64-2.02-66.el8'
    'grub2-efi-x64-2.02-66.el8_0.1'
    'grub2-efi-x64-2.02-78.el8'
    'grub2-efi-x64-2.02-78.el8_1.1'
    'grub2-efi-x64-2.02-81.el8'
    'grub2-efi-x64-2.02-82.el8_2.1'
    'grub2-efi-x64-2.02-84.el8'
    'grub2-efi-x64-2.02-87.el8_0'
    'grub2-efi-x64-2.02-87.el8_1.1'
    'grub2-efi-x64-2.02-87.el8_1'
    'grub2-efi-x64-2.02-87.el8_2.2'
    'grub2-efi-x64-2.02-87.el8_2'
    'grub2-efi-x64-2.02-88.el8'
    'grub2-efi-x64-2.02-90.el8'
    'grub2-efi-ia32-2.02-0.64.el7'
    'grub2-efi-ia32-2.02-0.65.el7_4.2'
    'grub2-efi-ia32-2.02-0.65.el7_4.3'
    'grub2-efi-ia32-2.02-0.65.el7_5.4'
    'grub2-efi-ia32-2.02-0.76.el7.1'
    'grub2-efi-ia32-2.02-0.76.el7'
    'grub2-efi-ia32-2.02-0.76.el7_6.2'
    'grub2-efi-ia32-2.02-0.80.el7'
    'grub2-efi-ia32-2.02-0.81.el7'
    'grub2-efi-ia32-2.02-0.86.el7'
    'grub2-efi-ia32-2.02-0.86.el7_2'
    'grub2-efi-ia32-2.02-0.86.el7_4'
    'grub2-efi-ia32-2.02-0.86.el7_6'
    'grub2-efi-ia32-2.02-0.86.el7_7'
    'grub2-efi-ia32-2.02-0.86.el7_8'
    'grub2-efi-ia32-2.02-0.87.el7'
    'grub2-efi-ia32-2.02-66.el8'
    'grub2-efi-ia32-2.02-66.el8_0.1'
    'grub2-efi-ia32-2.02-78.el8'
    'grub2-efi-ia32-2.02-78.el8_1.1'
    'grub2-efi-ia32-2.02-81.el8'
    'grub2-efi-ia32-2.02-82.el8_2.1'
    'grub2-efi-ia32-2.02-84.el8'
    'grub2-efi-ia32-2.02-87.el8_0'
    'grub2-efi-ia32-2.02-87.el8_1.1'
    'grub2-efi-ia32-2.02-87.el8_1'
    'grub2-efi-ia32-2.02-87.el8_2.2'
    'grub2-efi-ia32-2.02-87.el8_2'
    'grub2-efi-ia32-2.02-88.el8'
    'grub2-efi-ia32-2.02-90.el8'
    'grub2-efi-aa64-2.02-0.65.el7_4.2'
    'grub2-efi-aa64-2.02-0.75.el7'
    'grub2-efi-aa64-2.02-0.76.el7.1'
    'grub2-efi-aa64-2.02-0.76.el7'
    'grub2-efi-aa64-2.02-0.76.el7_6.2'
    'grub2-efi-aa64-2.02-0.86.el7_6'
    'grub2-efi-aa64-2.02-66.el8'
    'grub2-efi-aa64-2.02-66.el8_0.1'
    'grub2-efi-aa64-2.02-78.el8'
    'grub2-efi-aa64-2.02-78.el8_1.1'
    'grub2-efi-aa64-2.02-81.el8'
    'grub2-efi-aa64-2.02-82.el8_2.1'
    'grub2-efi-aa64-2.02-84.el8'
    'grub2-efi-aa64-2.02-87.el8_2'
    'grub2-efi-aa64-2.02-88.el8'
    'grub2-efi-aa64-2.02-90.el8'
    'shim-0.7-4.el7'
    'shim-0.7-5.2.el7'
    'shim-0.7-5.el7'
    'shim-0.7-8.el7_0'
    'shim-0.9-1.el7'
    'shim-0.9-2.el7'
    'shim-12-1.el7'
    'shim-15-1.el7'
    'shim-15-5.el7'
    'shim-15-7.el7_9'
    'shim-15-8.el7'
    'shim-15-8.el7_2'
    'shim-15-8.el7_3'
    'shim-15-9.el7'
    'shim-15-14.el8_0'
    'shim-15-14.el8_1'
    'shim-15-14.el8_2'
    'shim-15-15.el8'
    'shim-15-15.el8_0'
    'shim-15-15.el8_1'
    'shim-15-15.el8_2'
    'shim-15-16.el8'
    'shim-15-5'
    'shim-15-8'
    'shim-15-11'
    'shim-15-12'
    'shim-x64-12-1.el7'
    'shim-x64-15-1.el7'
    'shim-x64-15-2.el7'
    'shim-x64-15-7.el7_8'
    'shim-x64-15-8.el7_4'
    'shim-x64-15-8.el7_6'
    'shim-x64-15-8.el7_7'
    'shim-x64-15-8.el7_8'
    'shim-x64-15-11.el7'
    'shim-x64-15-14.el8_0'
    'shim-x64-15-14.el8_1'
    'shim-x64-15-14.el8_2'
    'shim-x64-15-15.el8'
    'shim-x64-15-15.el8_0'
    'shim-x64-15-15.el8_1'
    'shim-x64-15-15.el8_2'
    'shim-x64-15-16.el8'
    'shim-x64-15-5'
    'shim-x64-15-8'
    'shim-x64-15-11'
    'shim-x64-15-12'
    'shim-ia32-12-1.el7'
    'shim-ia32-15-1.el7'
    'shim-ia32-15-2.el7'
    'shim-ia32-15-7.el7_8'
    'shim-ia32-15-8.el7_4'
    'shim-ia32-15-8.el7_6'
    'shim-ia32-15-8.el7_7'
    'shim-ia32-15-8.el7_8'
    'shim-ia32-15-11.el7'
    'shim-ia32-15-14.el8_0'
    'shim-ia32-15-14.el8_1'
    'shim-ia32-15-14.el8_2'
    'shim-ia32-15-15.el8'
    'shim-ia32-15-15.el8_0'
    'shim-ia32-15-15.el8_1'
    'shim-ia32-15-15.el8_2'
    'shim-ia32-15-16.el8'
    'shim-ia32-15-5'
    'shim-ia32-15-8'
    'shim-ia32-15-11'
    'shim-ia32-15-12'
)

PKG_NAMES_SHIM=(
    'shim' 'shim-ia32' 'shim-x64'
)
PKG_NAMES_GRUB=(
    'grub2' 'grub2-pc' 'grub2-efi' 'grub2-efi-x64' 'grub2-efi-ia32' 'grub2-efi-aa64'
)


basic_args() {
    # Parses basic commandline arguments and sets basic environment.
    #
    # Args:
    #     parameters - an array of commandline arguments
    #
    # Side effects:
    #     Exits if --help parameters is used
    #     Sets COLOR constants and debug variable

    local parameters=( "$@" )

    RED="\\033[1;31m"
    GREEN="\\033[1;32m"
    BOLD="\\033[1m"
    RESET="\\033[0m"
    for parameter in "${parameters[@]}"; do
        if [[ "$parameter" == "-h" || "$parameter" == "--help" ]]; then
            echo "Usage: $( basename "$0" ) [-n | --no-colors] [-d | --debug]"
            exit 1
        elif [[ "$parameter" == "-n" || "$parameter" == "--no-colors" ]]; then
            RED=""
            GREEN=""
            BOLD=""
            RESET=""
        elif [[ "$parameter" == "-d" || "$parameter" == "--debug" ]]; then
            debug=true
        fi
    done
}


basic_reqs() {
    # Prints common disclaimer and checks basic requirements.
    #
    # Args:
    #     CVE - string printed in the disclaimer
    #
    # Side effects:
    #     Exits when 'rpm' command is not available

    local CVE="$1"

    # Disclaimer
    echo
    echo -e "${BOLD}This script (v$VERSION) is primarily designed to detect $CVE on supported"
    echo -e "Red Hat Enterprise Linux systems and kernel packages."
    echo -e "Result may be inaccurate for other RPM based systems.${RESET}"
    echo

    # RPM is required
    if ! command -v rpm &> /dev/null; then
        echo "'rpm' command is required, but not installed. Exiting."
        exit 1
    fi
}


check_supported_kernel() {
    # Checks if running kernel is supported.
    #
    # Args:
    #     running_kernel - kernel string as returned by 'uname -r'
    #
    # Side effects:
    #     Exits when running kernel is obviously not supported

    local running_kernel="$1"

    # Check supported platform
    if [[ "$running_kernel" != *".el"[6-8]* ]]; then
        echo -e "${RED}This script is meant to be used only on RHEL 6-8.${RESET}"
        exit 1
    fi
}


set_default_values() {
    result=0
    vulnerable=0
    bootproblem=0
    reason=""
    secure_boot_active=0
}


check_dmesg() {
    # Checks if dmesg log or dmesg command contains the given string.
    #
    # Args:
    #     search_string - string to look for in dmesg and dmesg log
    #
    # Returns:
    #     1 if given string present, otherwise 0
    #
    # Notes:
    #     MOCK_DMESG_PATH can be used to mock /var/log/dmesg file

    local search_string="$1"
    local dmesg_log=${MOCK_DMESG_PATH:-/var/log/dmesg}
    local dmesg_cmd
    dmesg_cmd=$( dmesg )
    local journalctl_output
    local journalctl_success


    if grep --quiet -F "$search_string" "$dmesg_log" 2>/dev/null ; then
        return 1
    fi
    if grep --quiet -F "$search_string" <<< "$dmesg_cmd"; then
        return 1
    fi

    if command -v journalctl &> /dev/null; then
        journalctl_output="$( journalctl -k 2>/dev/null )"
        # Store inverted output value as a flag that is true when the command ended with a zero return value.
        # shellcheck disable=SC2181
        journalctl_success=$(( ! $? ))
        if (( journalctl_success )) ; then
            if grep --quiet -F "$search_string" <<< "$journalctl_output"; then
                return 1
            fi
        fi
    fi

    return 0
}


secure_boot_dmesg() {
    # Uses dmesg to detect whether secure boot is active.
    #
    # Side effects:
    #     Sets the `secure_boot_active_dmesg` variable to 1 if secure boot is active
    #     according to dmesg, or to 0 if it is not.

    check_dmesg "Secure boot enabled"
    # Store result as a flag, 1 as True, 0 as False
    # shellcheck disable=SC2181
    secure_boot_active_dmesg=$(( $? ))
}


secure_boot_mokutil() {
    # Uses mokutil to detect whether secure boot is active.
    #
    # Side effects:
    #     Sets the `secure_boot_active_mokutil` variable to 1 if secure boot is active
    #     according to mokutil, or to 0 if it is not or if mokutil is not installed.

    secure_boot_active_mokutil=0
    if command -v mokutil &> /dev/null; then
        if [[ "$( mokutil --sb-state 2>&1 )" == "SecureBoot enabled" ]]; then
            secure_boot_active_mokutil=1
        fi
    fi
}


secure_boot_firmware() {
    # Uses /sys/firmware/efi/efivars/SecureBoot-* to detect whether secure boot is active.
    #
    # Side effects:
    #     Sets the `secure_boot_active_firmware` variable to 1 if secure boot is active
    #     according to /sys/firmware/efi/efivars/SecureBoot-*, or to 0 if it is not or
    #     if the path is not available.
    #
    # Notes:
    #     MOCK_EFI_PATH can be used to mock /sys/firmware/efi/efivars directory

    secure_boot_active_firmware=0
    # See https://github.com/koalaman/shellcheck/wiki/SC2144 for the reason of this for/if/break type of file existence check
    for sb_efivar in "${MOCK_EFI_PATH:-/sys/firmware/efi/efivars}"/SecureBoot-*; do
        if [[ -f "$sb_efivar" ]]; then
            if [[ "$(od --address-radix=n --format=u1 "$sb_efivar" | awk '{print $5}')" == "1" ]]; then
                secure_boot_active_firmware=1
            fi
            break
        fi
    done
}


check_package() {
    # Checks if installed package is in list of vulnerable packages.
    #
    # Args:
    #     installed_packages - installed packages string as returned by 'rpm -qa package'
    #                          (may be multiline)
    #     vulnerable_versions - an array of vulnerable versions
    #
    # Prints:
    #     First vulnerable package string as returned by 'rpm -qa package', or nothing

    # Convert to array, use word splitting on purpose
    # shellcheck disable=SC2206
    local installed_packages=( $1 )
    shift
    local vulnerable_versions=( "$@" )

    for tested_package in "${vulnerable_versions[@]}"; do
        for installed_package in "${installed_packages[@]}"; do
            installed_package_without_arch="${installed_package%.*}"
            if [[ "$installed_package_without_arch" == "$tested_package" ]]; then
                echo "$installed_package"
                return 0
            fi
        done
    done
}


get_installed_packages() {
    # Checks for installed packages. Compatible with RHEL5.
    #
    # Args:
    #     package_names - an array of package name strings
    #
    # Prints:
    #     Liness with N-V-R.A strings of the installed packages.

    local package_names=( "$@" )

    rpm -qa --queryformat="%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n" "${package_names[@]}"
}


get_installed_package_names() {
    # Checks for installed packages and returns the names if installed. Compatible with RHEL5.
    #
    # Args:
    #     package_names - an array of package name strings
    #
    # Prints:
    #     Lines with the names of the installed packages.

    local package_names=( "$@" )

    rpm -qa --queryformat="%{NAME}\n" "${package_names[@]}" | uniq
}


installed_rpms_shim() {
    # Prints installed package versions for all the relevant packages.

    get_installed_packages "${PKG_NAMES_SHIM[@]}"
}


installed_rpms_grub() {
    # Prints installed package versions for all the relevant packages.

    get_installed_packages "${PKG_NAMES_GRUB[@]}"
}


installed_rpm_names_shim() {
    # Prints installed package versions for all the relevant packages.

    get_installed_package_names "${PKG_NAMES_SHIM[@]}"
}


installed_rpm_names_grub() {
    # Prints installed package versions for all the relevant packages.

    get_installed_package_names "${PKG_NAMES_GRUB[@]}"
}


check_fixed_rpms_all() {
    # Detects if all installed packages are non-vulnerable
    #
    # Args:
    #     installed_packages - installed packages string as returned by 'rpm -qa package'
    #                          (may be multiline)
    #
    # Returns:
    #     - 1 if no RPM from the group is installed at all
    #     - 1 if all RPMs of the group that are installed are in non-vulnerable versions
    #     - 0 if at least one RPM of the group that is installed is in a vulnerable version

    # Convert to array, use word splitting on purpose
    # shellcheck disable=SC2206
    local installed_packages=( $1 )
    local vulnerable_package
    local ret=1
    if [[ "${installed_packages[*]}" ]]; then
        for nvra in "${installed_packages[@]}"; do
            vulnerable_package=$( check_package "$nvra" "${VULNERABLE_VERSIONS[@]}" )
            if [[ "$vulnerable_package" ]] ; then
                ret=0  # a single vulnerable package flips the flag to 0
                break
            fi
        done
    fi
    return "$ret"
}


fixed_rpm_shim() {
    # Detects if a fixed version of the RPM is installed.
    #
    # Side effects:
    #     Sets the `fixed_shim` variable to 1 or 0.
    #         - 1 if no RPM from the group is installed at all
    #         - 1 if all RPMs of the group that are installed are in non-vulnerable versions
    #         - 0 if at least one RPM of the group that is installed is in a vulnerable version

    check_fixed_rpms_all "$installed_shim"
    # Store result as a flag, 1 as True, 0 as False
    # shellcheck disable=SC2181
    fixed_shim=$(( $? ))
}


fixed_rpm_grub() {
    # Detects if a fixed version of the RPM is installed.
    #
    # Side effects:
    #     Sets the `fixed_grub` variable to 1 or 0.
    #         - 1 if no RPM from the group is installed at all
    #         - 1 if all RPMs of the group that are installed are in non-vulnerable versions
    #         - 0 if at least one RPM of the group that is installed is in a vulnerable version

    check_fixed_rpms_all "$installed_grub"
    # Store result as a flag, 1 as True, 0 as False
    # shellcheck disable=SC2181
    fixed_grub=$(( $? ))
}


parse_facts() {
    # Gathers all available information and stores it in global variables. Only store facts and
    # do not draw conclusion in this function for better maintainability.
    #
    # Side effects:
    #     Sets many global boolean flags and content variables

    installed_shim=$(installed_rpms_shim)
    installed_grub=$(installed_rpms_grub)

    fixed_rpm_shim        # sets flag fixed_shim
    fixed_rpm_grub        # sets flag fixed_grub
    secure_boot_dmesg     # sets flag secure_boot_active_dmesg
    secure_boot_mokutil   # sets flag secure_boot_active_mokutil
    secure_boot_firmware  # sets flag secure_boot_active_firmware
}


draw_conclusions() {
    # Draws conclusions based on available system data.
    #
    # Side effects:
    #     Sets many global boolean flags and content variables

    # All three work the same. They are all used here because relying only on
    # one way of detection might break on some non-standard configurations, e.g.:
    # * all dmesg logging is disabled and the system runs long enough so that dmesg wraps
    # * mokutil on an UEFI system is uninstalled
    # * efivars on an UEFI system is unmounted
    secure_boot_active=$(( secure_boot_active_dmesg || secure_boot_active_mokutil || secure_boot_active_firmware ))

    local flag_installed_shim=0
    local flag_installed_grub=0
    if [[ "$installed_shim" ]]; then
        flag_installed_shim=1
    fi
    if [[ "$installed_grub" ]]; then
        flag_installed_grub=1
    fi

    if (( ! secure_boot_active )); then
        vulnerable=0
        bootproblem=0
        result=0
        reason="Secure Boot is not enabled."
    elif (( ( ! flag_installed_shim || fixed_shim ) && fixed_grub )); then
        vulnerable=0
        bootproblem=0
        result=0
        reason="Vulnerable package versions not installed."
    elif (( ! flag_installed_grub )); then
        vulnerable=0
        bootproblem=0
        result=0
        reason="GRUB 2 is not installed."
    elif (( ( flag_installed_shim && fixed_shim ) && ( flag_installed_grub && !fixed_grub ) )); then
        vulnerable=1
        bootproblem=1
        (( result |= 2 ))
        reason="Only a partial update of the affected packages has been applied."
    elif (( ( flag_installed_shim && !fixed_shim ) && ( flag_installed_grub && fixed_grub ) )); then
        vulnerable=1
        bootproblem=0
        (( result |= 4 ))
        reason="Only a partial update of the affected packages has been applied."
    else
        vulnerable=1
        bootproblem=0
        (( result |= 8 ))
        reason="Vulnerable versions of the affected packages are installed."
    fi
}


debug_print() {
    # Prints selected variables when debugging is enabled.

    variables=( running_kernel rhel fixed_shim fixed_grub secure_boot_active_dmesg secure_boot_active_mokutil secure_boot_active_firmware vulnerable bootproblem result )
    for variable in "${variables[@]}"; do
        echo "$variable = *${!variable}*"
    done
    echo
}


if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
    basic_args "$@"
    basic_reqs "CVE-2020-14372"
    running_kernel=$( uname -r )
    check_supported_kernel "$running_kernel"

    set_default_values
    parse_facts
    draw_conclusions

    # Debug prints
    if [[ "$debug" ]]; then
        debug_print
    fi

    echo
    echo "Installed shim:"
    if [[ "$installed_shim" ]]; then
        echo "$installed_shim"
    else
        echo "(applicable shim not installed)"
    fi

    echo "Installed grub2:"
    if [[ "$installed_grub" ]]; then
        echo "$installed_grub"
    else
        echo "(no grub2 installed)"
    fi

    echo

    # Results
    if (( vulnerable )); then
        echo -e "${RED}This system is vulnerable.${RESET}"
        echo
        echo "* $reason"
        echo "* Secure boot is ENABLED."
        echo -e "* The following installed packages are affected:"
        echo
        if (( ! fixed_grub )); then
            installed_rpm_names_grub
        fi
        if (( ! fixed_shim )); then
            installed_rpm_names_shim
        fi
        echo
        if (( bootproblem )); then
            echo -e "${RED}Additionally, the system might be unable to boot correctly in its current configuration.${RESET} To resolve the issue, update all the listed packages."
            echo
        fi
        echo -e "For more information about this vulnerability, see:"
        echo -e "${ARTICLE}"

    else
        echo -e "${GREEN}This system is not vulnerable.${RESET}"
        echo
        echo "Reason: $reason"
    fi

    exit "$result"
fi
